package com.alogic.textfilter;

import com.alogic.cache.CacheObject;
import com.alogic.cache.LocalCacheStore;
import com.alogic.load.Loadable;
import com.alogic.load.Store;
import com.anysoft.util.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import java.util.Map;

/**
 * 文本过滤器
 *
 * @since 1.6.13.10 [20200827 duanyy]
 *
 * @version 1.6.13.22 [20201026 duanyy] <br>
 * - 增加缓存filter结果的功能
 */
public interface TextFilter extends Loadable {

    /**
     * 文本过滤
     * @param text 原始文本
     * @param ctx 上下文变量
     * @return 处理之后的文本
     */
    public String doFilter(String text, Properties ctx);

    /**
     * 虚基类
     */
    public static abstract class Abstract extends Loadable.Abstract implements TextFilter,XMLConfigurable,Configurable{

        /**
         * logger of slf4j
         */
        protected Logger LOG = LoggerFactory.getLogger(TextFilter.class);

        protected boolean cacheEnable = false;
        protected Store<CacheObject> cacheStore = null;

        protected String id = "default";
        @Override
        public void configure(Element e, Properties p) {
            XmlElementProperties props = new XmlElementProperties(e,p);
            configure(props);

            if (cacheEnable) {
                Element cacheElem = XmlTools.getFirstElementByPath(e, "cache");
                if (cacheElem != null) {
                    Factory<Store<CacheObject>> f = new Factory<Store<CacheObject>>();

                    try {
                        cacheStore = f.newInstance(cacheElem, props, "module");
                    } catch (Exception ex) {
                        LOG.error("Can not create cache instance:" + XmlTools.node2String(cacheElem));
                    }
                }
                if (cacheStore == null){
                    cacheStore = new LocalCacheStore();
                    cacheStore.configure(props);
                }
            }

            onConfigure(e,props);
        }
        @Override
        public String getId() {
            return id;
        }

        @Override
        public void configure(Properties p) {
            id = PropertiesConstants.getString(p,"id",id);
            cacheEnable = PropertiesConstants.getBoolean(p,"cacheEnable",cacheEnable);
        }

        @Override
        public String doFilter(String value, Properties ctx) {
            if (cacheEnable && cacheStore != null){
                String digest = DigestUtils.md5Hex(value);
                CacheObject found = cacheStore.load(digest,true);
                if (found == null){
                    synchronized (this){
                        found = cacheStore.load(digest,true);
                        if (found == null){
                            String result = onFilter(value,ctx);
                            found = new CacheObject.Simple(digest);
                            found.hSet(CacheObject.DEFAULT_GROUP,CacheObject.DEFAULT_GROUP,result,true);
                            cacheStore.save(digest,found,true);
                            return result;
                        }
                    }
                }
                return found.hGet(CacheObject.DEFAULT_GROUP,CacheObject.DEFAULT_GROUP,"");
            }else {
                return onFilter(value,ctx);
            }
        }

        /**
         * 处理Filter
         * @param value 原始值
         * @param ctx 上下文
         * @return Filter之后的值
         */
        protected abstract String onFilter(String value,Properties ctx);
        protected abstract void onConfigure(Element e, Properties p);

        @Override
        public void report(Element xml) {
            if (xml != null){
                XmlTools.setString(xml, "module", getClass().getName());
                XmlTools.setString(xml,"id",getId());
            }
        }

        @Override
        public void report(Map<String, Object> json) {
            if (json != null){
                JsonTools.setString(json,"module",getClass().getName());
                JsonTools.setString(json,"id",getId());
            }
        }
    }

    /**
     * 转义
     */
    public abstract static class Escape extends Abstract {
        protected boolean escape = true;

        @Override
        public void configure(Properties p){
            super.configure(p);
            escape = PropertiesConstants.getBoolean(p,"escape",escape);
        }

        @Override
        public String onFilter(String value, Properties ctx) {
            return escape?escape(value):unescape(value);
        }

        protected abstract String escape(String value);

        protected abstract String unescape(String value);

        @Override
        protected void onConfigure(Element e, Properties p) {

        }
    }
}
